package models

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"log"
	"net"
	"net/http"
	"pro_server/utils"
	"strconv"
	"sync"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/go-redis/redis/v8"
	"github.com/gorilla/websocket"
	"github.com/spf13/viper"
	"gopkg.in/fatih/set.v0"
	//"gorm.io/gorm"
)

// 消息
type Message struct {
	UserId     int    //发送者
	TargetId   int    //接受者
	Type       int    //发送类型  1私聊  2群聊  3心跳 4时间
	Media      int    //消息类型  1文字 2表情包 3语音 4图片 /表情包
	Content    string //消息内容
	CreateTime uint64 //创建时间 `json:"CreateTime"` 
	ReadTime   uint64 //读取时间 `json:"ReadTime"` 

}

func (table *Message) TableName() string {
	return "message"
}

// const (
// 	HeartbeatMaxTime = 1 * 60
// )

type Node struct {
	Conn          *websocket.Conn //连接
	Addr          string          //客户端地址
	FirstTime     uint64          //首次连接时间
	HeartbeatTime uint64          //心跳时间
	LoginTime     uint64          //登录时间
	DataQueue     chan []byte     //消息
	GroupSets     set.Interface   //好友 / 群
}

// 映射关系
var clientMap map[int]*Node = make(map[int]*Node, 0)

// 读写锁
var rwLocker sync.RWMutex

// 需要 ：发送者ID ，接受者ID ，消息类型，发送的内容，发送类型
func Chat(writer http.ResponseWriter, request *http.Request) {
	//1.  获取参数 并 检验 token 等合法性
	//token := query.Get("token")
	query := request.URL.Query()
	Id := query.Get("Id")
	//userId, _ := strconv.ParseInt(Id, 10, 64)
	userId, err := strconv.Atoi(Id)
	if err != nil {
		// 若转换出错，返回错误信息给客
		http.Error(writer, "Invalid Id parameter", http.StatusBadRequest)
		return
	}
	//msgType := query.Get("type")
	//targetId := query.Get("targetId")
	//	context := query.Get("context")
	isvalida := true //checkToke()  待.........
	conn, err := (&websocket.Upgrader{
		//token 校验
		CheckOrigin: func(r *http.Request) bool {
			return isvalida
		},
	}).Upgrade(writer, request, nil)
	if err != nil {
		fmt.Println(err)
		return
	}
	//2.获取conn
	currentTime := uint64(time.Now().Unix())
	node := &Node{
		Conn:          conn,
		Addr:          conn.RemoteAddr().String(), //客户端地址
		HeartbeatTime: currentTime,                //心跳时间
		LoginTime:     currentTime,                //登录时间
		DataQueue:     make(chan []byte, 50),
		GroupSets:     set.New(set.ThreadSafe),
	}
	//3. 用户关系
	//4. userid 跟 node绑定 并加锁
	rwLocker.Lock()
	clientMap[userId] = node
	rwLocker.Unlock()
	//5.完成发送逻辑
	go sendProc(node)
	//6.完成接受逻辑
	//go recvProc(node)
	go test(node)
	//7.加入在线用户到缓存
	SetUserOnlineInfo("online_"+Id, []byte(node.Addr), time.Duration(viper.GetInt("timeout.RedisOnlineTime"))*time.Hour)

	//sendMsg(userId, []byte("欢迎进入聊天系统"))

}

func sendProc(node *Node) {
	for {
		select {
		case data := <-node.DataQueue:
			fmt.Println("[ws]sendProc >>>> msg :", string(data))
			err := node.Conn.WriteMessage(websocket.TextMessage, data)
			if err != nil {
				fmt.Println(err)
				return
			}
		}
	}
}

func test(node *Node) {
	for {
		messageType, data, err := node.Conn.ReadMessage()
		if err != nil {
			if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway, websocket.CloseAbnormalClosure) {
				log.Printf("意外关闭连接: %v", err)
			} else {
				log.Printf("客户端正常断开连接: %v", err)
			}
			break
		}

		switch messageType {
		case websocket.BinaryMessage:
			// 处理二进制消息
			// 处理二进制消息，根据 \n 分割数据
			//fmt.Println(data)
			delimiter := []byte("\n")
			name, fileData, _ := splitBinaryData(data, delimiter)
			if name != "" {
				utils.SaveBinaryDataToDir(fileData, name)
			}
		case websocket.TextMessage:
			// 处理文本消息
			msg := Message{}
			err = json.Unmarshal(data, &msg)
			if err != nil {
				fmt.Println(err)
			}
			//心跳检测 msg.Media == -1 || msg.Type == 3
			if msg.Type == 3 {
				currentTime := uint64(time.Now().Unix())
				node.Heartbeat(currentTime)
			} else {
				dispatch(data)
				broadMsg(data) //todo 将消息广播到局域网
				fmt.Println("[ws] recvProc <<<<< ", string(data))
			}
		}
	}
}

// splitBinaryData 根据分隔符分割二进制数据为名字和数据，同时将名字转换为字符串
func splitBinaryData(data []byte, delimiter []byte) (name string, remainingData []byte, found bool) {
	// 查找分隔符在二进制数据中的位置
	index := bytes.Index(data, delimiter)
	if index == -1 {
		return "", nil, false
	}
	// 分割数据并将名字转换为字符串
	name = string(data[:index])
	remainingData = data[index+len(delimiter):]
	return name, remainingData, true
}

func recvProc(node *Node) {
	for {
		_, data, err := node.Conn.ReadMessage()
		if err != nil {
			fmt.Println(err)
			return
		}
		msg := Message{}
		err = json.Unmarshal(data, &msg)
		if err != nil {
			fmt.Println(err)
		}
		//心跳检测 msg.Media == -1 || msg.Type == 3
		if msg.Type == 3 {
			currentTime := uint64(time.Now().Unix())
			node.Heartbeat(currentTime)
		} else {
			dispatch(data)
			broadMsg(data) //todo 将消息广播到局域网
			fmt.Println("[ws] recvProc <<<<< ", string(data))
		}

	}
}

var udpsendChan chan []byte = make(chan []byte, 1024)

func broadMsg(data []byte) {
	udpsendChan <- data
}

func init() {
	go udpSendProc()
	go udpRecvProc()
	fmt.Println("init goroutine ")
}

// 完成udp数据发送协程
func udpSendProc() {
	con, err := net.DialUDP("udp", nil, &net.UDPAddr{
		IP:   net.IPv4(192, 168, 0, 255),
		Port: viper.GetInt("port.udp"),
	})
	defer con.Close()
	if err != nil {
		fmt.Println(err)
	}
	for {
		select {
		case data := <-udpsendChan:
			fmt.Println("udpSendProc  data :", string(data))
			_, err := con.Write(data)
			if err != nil {
				fmt.Println(err)
				return
			}
		}
	}

}

// 完成udp数据接收协程
func udpRecvProc() {
	con, err := net.ListenUDP("udp", &net.UDPAddr{
		IP:   net.IPv4zero,
		Port: viper.GetInt("port.udp"),
	})
	if err != nil {
		fmt.Println(err)
	}
	defer con.Close()
	for {
		var buf [512]byte
		n, err := con.Read(buf[0:])
		if err != nil {
			fmt.Println(err)
			return
		}
		fmt.Println("udpRecvProc  data :", string(buf[0:n]))
		dispatch(buf[0:n])
	}
}


func dispatch(data []byte) {
    msg := Message{}
    // 先反序列化 JSON 数据到 msg 结构体
    err := json.Unmarshal(data, &msg)
    if err != nil {
        fmt.Println(err)
        return
    }
    // 更新 CreateTime 字段为当前时间
    msg.CreateTime = uint64(time.Now().Unix())
	//如果为时间消息，将正文替换为时间戳
	if msg.Type == 4 {
		msg.Content = strconv.FormatUint(msg.CreateTime, 10)
	}
    // 将更新后的 msg 结构体重新序列化为 JSON 字节切片
    updatedData, err := json.Marshal(msg)
    if err != nil {
        fmt.Println("重新序列化出错:", err)
        return
    }

    switch msg.Type {
    case 1,4: // 私信
        fmt.Println("dispatch  data :", string(updatedData))
        sendMsg(msg.TargetId, updatedData)
    case 2: // 群发
        // sendGroupMsg(msg.TargetId, updatedData) // 发送的群 ID ，消息内容
        // case 4: // 心跳
        //  node.Heartbeat()
        // case 4:
        //
    }
}

func sendMsg(userId int, msg []byte) {

	rwLocker.RLock()
	node, ok := clientMap[userId]
	rwLocker.RUnlock()
	jsonMsg := Message{}
	json.Unmarshal(msg, &jsonMsg)
	ctx := context.Background()
	targetIdStr := strconv.Itoa(int(userId))
	userIdStr := strconv.Itoa(int(jsonMsg.UserId))
	jsonMsg.CreateTime = uint64(time.Now().Unix())
	
	r, err := utils.Red.Get(ctx, "online_"+userIdStr).Result()
	if err != nil {
		fmt.Println(err)
	}
	if r != "" {
		if ok {
			fmt.Println("sendMsg >>> userID: ", userId, "  msg:", string(msg))
			node.DataQueue <- msg
		}
	}
	var key string
	if userId > jsonMsg.UserId {
		key = "msg_" + userIdStr + "_" + targetIdStr
	} else {
		key = "msg_" + targetIdStr + "_" + userIdStr
	}
	res, err := utils.Red.ZRevRange(ctx, key, 0, -1).Result()
	if err != nil {
		fmt.Println(err)
	}
	score := float64(cap(res)) + 1
	ress, e := utils.Red.ZAdd(ctx, key, &redis.Z{score, msg}).Result() //jsonMsg
	//res, e := utils.Red.Do(ctx, "zadd", key, 1, jsonMsg).Result() //备用 后续拓展 记录完整msg
	if e != nil {
		fmt.Println(e)
	}
	fmt.Println(ress)
}

// 需要重写此方法才能完整的msg转byte[]
func (msg Message) MarshalBinary() ([]byte, error) {
	return json.Marshal(msg)
}

// RedisMsg 处理函数，用于解析客户端数据并返回聊天信息给客户端
func RedisMsg(c *gin.Context) {
	// Request 定义客户端请求的数据结构
	type Request struct {
		MsgId string `json:"MsgId"`
	}
	// ChatMessage 定义包含消息内容和分数的数据结构
	type ChatMessage struct {
		Message string  `json:"message"`
		Score   float64 `json:"score"`
	}
	// 定义响应数据结构
	type Response struct {
		MsgId   string        `json:"MsgId"`
		MsgData []ChatMessage `json:"MsgData"`
	}

	// 定义请求结构体
	var req Request
	// 使用 c.BindJSON 绑定 JSON 数据到 Request 结构体
	if err := c.BindJSON(&req); err != nil {
		// 若绑定失败，返回 400 Bad Request 错误
		c.JSON(http.StatusBadRequest, gin.H{
			"error": "无效的 JSON 数据: " + err.Error(),
		})
		return
	}

	// 从 Redis 有序集合中获取最多 150 条消息及其分数
	membersWithScores, err := utils.Red.ZRangeWithScores(c, req.MsgId, 0, 149).Result()
	if err != nil {
		log.Printf("Failed to get chat messages for key %s: %v", req.MsgId, err)
		c.JSON(http.StatusInternalServerError, gin.H{
			"error": "获取聊天消息失败: " + err.Error(),
		})
		return
	}

	// 如果获取的数据为空，则直接返回
	if membersWithScores == nil ||len(membersWithScores)==0 {
		//fmt.Println("msgId is null")
		return
	}

	messages := make([]ChatMessage, 0, len(membersWithScores))
	for _, memberWithScore := range membersWithScores {
		// 类型断言将 interface{} 类型的成员转换为 string 类型
		message, ok := memberWithScore.Member.(string)
		if !ok {
			log.Printf("Failed to convert member to string for key %s", req.MsgId)
			continue
		}
		messages = append(messages, ChatMessage{
			Message: message,
			Score:   memberWithScore.Score,
		})
	}

	// 构建响应数据
	resp := Response{
		MsgId:   req.MsgId,
		MsgData: messages,
	}

	// 将所有聊天信息以 JSON 格式发送回客户端
	c.JSON(http.StatusOK, resp)
}

func UpdataReadTime(msgId string,max_score float64,min_score float64) error {
    ctx := context.Background()

    // 获取分数在 min_score 到 max_score 范围内的成员
    members, err := utils.Red.ZRangeByScore(ctx, msgId, &redis.ZRangeBy{
        Min: strconv.FormatFloat(min_score, 'f', -1, 64),
        Max: strconv.FormatFloat(max_score, 'f', -1, 64),
    }).Result()
    if err != nil {
        return err
    }

    // 遍历每个成员并更新 ReadTime 字段
    for _, member := range members {
        var msg Message
        // 解析成员的 JSON 数据
        err := json.Unmarshal([]byte(member), &msg)
        if err != nil {
            return err
        }

        // 更新 ReadTime 字段为当前时间戳
        msg.ReadTime = uint64(time.Now().Unix())

        // 将更新后的数据转换为 JSON 字符串
        updatedJSON, err := json.Marshal(msg)
        if err != nil {
            return err
        }

        // 获取成员原来的分数
        score, err := utils.Red.ZScore(ctx, msgId, member).Result()
        if err != nil {
            return err
        }

        // 从有序集合中移除原来的成员
        err = utils.Red.ZRem(ctx, msgId, member).Err()
        if err != nil {
            return err
        }

        // 将更新后的成员重新添加到有序集合中，保持原分数
        err = utils.Red.ZAdd(ctx, msgId, &redis.Z{
            Score:  score,
            Member: string(updatedJSON),
        }).Err()
        if err != nil {
            return err
        }
    }

    return nil
}

// 更新用户心跳
func (node *Node) Heartbeat(currentTime uint64) {
	node.HeartbeatTime = currentTime
	return
}

// 清理超时连接
func CleanConnection(param interface{}) (result bool) {
	result = true
	defer func() {
		if r := recover(); r != nil {
			fmt.Println("cleanConnection err", r)
		}
	}()
	//fmt.Println("定时任务,清理超时连接 ", param)
	//node.IsHeartbeatTimeOut()
	currentTime := uint64(time.Now().Unix())
	for i := range clientMap {
		node := clientMap[i]
		if node.IsHeartbeatTimeOut(currentTime) {
			fmt.Println("心跳超时..... 关闭连接：", node)
			node.Conn.Close()
		}
	}
	return result
}

// 用户心跳是否超时
func (node *Node) IsHeartbeatTimeOut(currentTime uint64) (timeout bool) {
	if node.HeartbeatTime+viper.GetUint64("timeout.HeartbeatMaxTime") <= currentTime {
		fmt.Println("心跳超时。。。自动下线", node)
		timeout = true
	}
	return
}
